﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Security.Cryptography;
using System.Text;

namespace UNSWThink
{
   /// <summary>
   /// Object that contains all sorts of functions that could be helpful among different objects
   /// </summary>
   public class HelperMethods
   {
      /// <summary>
      /// Default constructor
      /// </summary>
      public HelperMethods()
      {

      }

      /// <summary>
      /// Finds the maximum ID with a given data table
      /// </summary>
      /// <param name="queriedTable">
      /// Data table containing IDs
      /// </param>
      /// <returns>
      /// The maximum ID that exists in the table
      /// </returns>
      public static int GetMaxID(DataTable queriedTable)
      {
         int currentMaxID = 0;

         if (queriedTable != null)
         {
            if (queriedTable.Columns.Contains("id"))
            {
               foreach (DataRow row in queriedTable.Rows)
               {
                  if ((int)row["id"] > currentMaxID)
                  {
                     currentMaxID = (int)row["id"];
                  }
               }
            }
         }

         return currentMaxID;
      }

      /// <summary>
      /// Gets the index of a record in a table with a given ID
      /// </summary>
      /// <param name="table">
      /// Table that's to be searched for a specific record
      /// </param>
      /// <param name="id">
      /// ID of the record
      /// </param>
      /// <returns>
      /// Index of the record in the table
      /// </returns>
      public static int GetIndexOfTableID(DataTable table, int id)
      {
         for (int i = 0; i < table.Rows.Count; i++)
         {
            if (table.Columns.Contains("id"))
            {
               if (Int32.Parse(table.Rows[i]["id"].ToString()) == id)
               {
                  return i;
               }
            }
         }

         return -1;
      }

      /// <summary>
      /// Generates a MD5 hash. This code is taken from the example shown at this address at MSDN
      /// (http://msdn.microsoft.com/en-us/library/system.security.cryptography.md5.aspx)
      /// </summary>
      /// <param name="input">
      /// Plain text string which is to be hashed
      /// </param>
      /// <returns>
      /// MD5 hash conversion of pain text input
      /// </returns>
      public static string GetMd5Hash(string input)
      {
         // Create a new instance of the MD5CryptoServiceProvider object.
         MD5 md5Hasher = MD5.Create();

         // Convert the input string to a byte array and compute the hash.
         byte[] data = md5Hasher.ComputeHash(Encoding.Default.GetBytes(input));

         // Create a new Stringbuilder to collect the bytes
         // and create a string.
         StringBuilder sBuilder = new StringBuilder();

         // Loop through each byte of the hashed data 
         // and format each one as a hexadecimal string.
         for (int i = 0; i < data.Length; i++)
         {
            sBuilder.Append(data[i].ToString("x2"));
         }

         // Return the hexadecimal string.
         return sBuilder.ToString();
      }

      /// <summary>
      /// Convert a week number (int) to it's string representation
      /// </summary>
      /// <param name="number">
      /// Week as a number (int)
      /// </param>
      /// <returns>
      /// Week as a string prefixed with "Week "
      /// </returns>
      public static string WeekNumberToString(int number)
      {
         switch (number)
         {
            case Defs.MISC_WEEK_INT:
               return Defs.MISC_WEEK_STRING;
            case Defs.REV_WEEK_INT:
               return Defs.REV_WEEK_STRING;
            default:
               return Defs.WEEK_STRING + number.ToString();
         }
      }

      /// <summary>
      /// Convert a week string to it's number (int) representation. 
      /// Only used when string format is "Week *" or "* Week"
      /// E.g. "Week 1" or "Revision Week"
      /// </summary>
      /// <param name="week">
      /// Week as a string in "Week *" or "* Week" format
      /// </param>
      /// <returns>
      /// Week as a number (int)
      /// </returns>
      public static int WeekStringToNumber(string week)
      {
         switch (week)
         {
            case Defs.MISC_WEEK_STRING:
               return Defs.MISC_WEEK_INT;
            case Defs.REV_WEEK_STRING:
               return Defs.REV_WEEK_INT;
            default:
               return Int32.Parse(week.Substring(Defs.WEEK_STRING.Length));
         }
      }

      /// <summary>
      /// Checks to see if the ID already exists in the table
      /// </summary>
      /// <param name="table">
      /// Data table that is to be searched
      /// </param>
      /// <param name="id">
      /// Search key
      /// </param>
      /// <returns>
      /// If the ID exists in the table or not
      /// </returns>
      public static bool IDExists(DataTable table, int id)
      {
         if (table.Columns.Contains("id"))
         {
            for (int i = 0; i < table.Rows.Count; i++)
            {
               if ((int)table.Rows[i]["id"] == id)
               {
                  return true;
               }
            }
         }
         return false;
      }

      /// <summary>
      /// Checks to see if the string based value exists in a table
      /// </summary>
      /// <param name="table">
      /// Data table that is to be checked
      /// </param>
      /// <param name="columnName">
      /// Name of the column that is to be checked
      /// </param>
      /// <param name="value">
      /// Value that is to be compared with. Must be in the same format as values in the 
      /// data tables column
      /// </param>
      /// <returns>
      /// Whether or not a match was found
      /// </returns>
      public static bool ValueExists(DataTable table, string columnName, string value)
      {
         if (table.Columns.Contains(columnName))
         {
            for (int i = 0; i < table.Rows.Count; i++)
            {
               if (table.Rows[i][columnName].ToString() == value)
               {
                  return true;
               }
            }
         }
         return false;
      }

      /// <summary>
      /// Converts a QuestionType enum format to its equivalent string format
      /// </summary>
      /// <param name="type">
      /// Question type as an enum
      /// </param>
      /// <returns>
      /// Question type as a string
      /// </returns>
      public static string QuestionTypeEnumToString(QuestionType type)
      {
         switch (type)
         {
            case QuestionType.MCQ:
               return QuestionType.MCQ.ToString();
            case QuestionType.OPN:
               return QuestionType.OPN.ToString();
            case QuestionType.TFQ:
               return QuestionType.TFQ.ToString();
            default:
               return QuestionType.OPN.ToString();
         }
      }

      /// <summary>
      /// Converts a QuestionType in string format to its equivalent enum format
      /// </summary>
      /// <param name="type">
      /// Question type as a string
      /// </param>
      /// <returns>
      /// Question type as an enum
      /// </returns>
      public static QuestionType QuestionTypeStringToEnum(string type)
      {
         switch (type.ToUpper())
         {
            case "MCQ":
               return QuestionType.MCQ;
            case "OPN":
               return QuestionType.OPN;
            case "TFQ":
               return QuestionType.TFQ;
            default:
               return QuestionType.OPN;
         }
      }

      /// <summary>
      /// Checks to see if all the names in the string array exists in the table
      /// </summary>
      /// <param name="table">
      /// Table that is to be checked
      /// </param>
      /// <param name="columnNames">
      /// Array of string formatted column names
      /// </param>
      /// <returns>
      /// Whether or not the all of the column names exist in the table
      /// </returns>
      public static bool CheckColumnsExists(DataTable table, string[] columnNames)
      {
         int numColumns = columnNames.Length;
         int checkCounter = 0;

         for (int i = 0; i < numColumns; i++)
         {
            if (table.Columns.Contains(columnNames[i]))
            {
               checkCounter++;
            }
         }

         if (checkCounter == numColumns)
         {
            return true;
         }
         else
         {
            return false;
         }
      }

      /// <summary>
      /// Updates a table that exists in program memory
      /// </summary>
      /// <param name="table">
      /// Data table that is to be modified
      /// </param>
      /// <param name="rowIndex">
      /// Index of the row that is to be modified
      /// </param>
      /// <param name="columnNames">
      /// Array of names of the columns that are going to be modified.
      /// Must be in the same order as the values in the values array
      /// </param>
      /// <param name="values">
      /// Array of the updated values.
      /// Must be in the same order as the names in the columns array
      /// </param>
      public static void UpdateTable(DataTable table, int rowIndex, string[] columnNames, object[] values)
      {
         if (columnNames.Length == values.Length)
         {
            for (int i = 0; i < columnNames.Length; i++)
            {
               table.Rows[rowIndex][columnNames[i]] = values[i];
            }
         }
      }

      /// <summary>
      /// Inserts a new row into a table that exists in program memory
      /// </summary>
      /// <param name="table">
      /// Data table that is to be modified
      /// </param>
      /// <param name="columnNames">
      /// Array of names of the columns that are going to be modified.
      /// Must be in the same order as the values in the values array
      /// </param>
      /// <param name="values">
      /// Array of the updated values.
      /// Must be in the same order as the names in the columns array
      /// </param>
      public static void InsertIntoTable(DataTable table, string[] columnNames, object[] values)
      {
         if (columnNames.Length == values.Length)
         {
            if (CheckColumnsExists(table, columnNames))
            {
               table.Rows.Add(values);
            }
         }
      }

      /// <summary>
      /// Deletes are row from a table that exists in program memory
      /// </summary>
      /// <param name="table">
      /// Data table that is to be modified
      /// </param>
      /// <param name="rowIndex">
      /// Index of the row that is to be removed
      /// </param>
      public static void DeleteFromTable(DataTable table, int rowIndex)
      {
         if ((rowIndex >= 0) && (rowIndex < table.Rows.Count))
         {
            table.Rows.RemoveAt(rowIndex);
         }
      }

      /// <summary>
      /// Prefixs an object to an existing object array. Should only be used for adding
      /// the ID's value to existing items
      /// </summary>
      /// <param name="newItem">
      /// Item that is to be prefixed to the array
      /// </param>
      /// <param name="existingItems">
      /// Array that contains the existing items
      /// </param>
      /// <returns>
      /// Object array with the new item prefixed at the front
      /// </returns>
      public static object[] CreateNewObjectArray(object newItem, object[] existingItems)
      {
         int newLength = existingItems.Length + 1;
         object[] newArray = new object[newLength];

         for (int i = 0; i < newLength; i++)
         {
            if (i == 0)
            {
               newArray[i] = newItem;
            }
            else
            {
               newArray[i] = existingItems[i - 1];
            }
         }

         return newArray;
      }

      /// <summary>
      /// Prefixes a string to an existing string array. Should only be used for adding
      /// the "id" to existing column names
      /// </summary>
      /// <param name="newItem">
      /// String that is to be prefixed to the array
      /// </param>
      /// <param name="existingItems">
      /// Array that contains the existing strings
      /// </param>
      /// <returns>
      /// String array with the new string prefixed at the front
      /// </returns>
      public static string[] CreateNewStringArray(string newItem, string[] existingItems)
      {
         int newLength = existingItems.Length + 1;
         string[] newArray = new string[newLength];

         for (int i = 0; i < newLength; i++)
         {
            if (i == 0)
            {
               newArray[i] = newItem;
            }
            else
            {
               newArray[i] = existingItems[i - 1];
            }
         }

         return newArray;
      }

      /// <summary>
      /// Updates a question data table by changing specific question's question number
      /// </summary>
      /// <param name="table">
      /// Data table that is to be modified
      /// </param>
      /// <param name="questionID">
      /// ID of the record that has been changed
      /// </param>
      public static void UpdateQuestionNumbers(DataTable table, int questionID)
      {
         int questionRowIndex = GetIndexOfTableID(table, questionID);
         if (questionRowIndex >= 0)
         {
            if (CheckColumnsExists(table, new string[1] {"number"}))
            {
               int questionNumber = Int32.Parse(table.Rows[questionRowIndex]["number"].ToString());
               for (int i = 0; i < table.Rows.Count; i++)
               {
                  int currentQuestionNum = Int32.Parse(table.Rows[i]["number"].ToString());
                  if (questionNumber < currentQuestionNum)
                  {
                     table.Rows[i]["number"] = (currentQuestionNum - 1);
                  }
               }
            }
         }
      }

      /// <summary>
      /// Converts from an array list of ResponseTotal to an array of strings containing
      /// all of the answers
      /// </summary>
      /// <param name="list">
      /// ResponseTotal list
      /// </param>
      /// <returns>
      /// String array continaing answers as strings
      /// </returns>
      public static string[] ParseStringArray(ArrayList list)
      {
         string[] temp = new string[list.Count];

         for (int i = 0; i < list.Count; i++)
         {
            temp[i] = ((ResponseTotal)list[i]).Answer;
         }

         return temp;
      }

      /// <summary>
      /// Converts values for a date into a combined string to be used when generating a DateTime
      /// </summary>
      /// <param name="year">
      /// Year
      /// </param>
      /// <param name="month">
      /// Month
      /// </param>
      /// <param name="day">
      /// Day
      /// </param>
      /// <param name="hour">
      /// Hour
      /// </param>
      /// <param name="minute">
      /// Minute
      /// </param>
      /// <param name="second">
      /// Second
      /// </param>
      /// <returns>
      /// String of date and time values in a 'nice' format
      /// </returns>
      public static string DateValuesToString(int year, int month, int day, 
         int hour, int minute, int second)
      {
         string convertedString = day + "/" + month + "/" + year + " " + 
            hour + ":" + minute + ":" + second;

         return convertedString;
      }
   }
}
